﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Azure.Core.TestFramework;
using NUnit.Framework;

namespace Azure.AI.DocumentIntelligence.Tests
{
    public class DocumentClassifierAdministrationLiveTests : DocumentIntelligenceLiveTestBase
    {
        public DocumentClassifierAdministrationLiveTests(bool isAsync)
            : base(isAsync)
        {
        }

        #region Build

        [RecordedTest]
        public async Task BuildClassifierWithBlobContentSource()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var classifierId = Recording.GenerateId();
            var description = "This classifier was generated by a .NET test.";
            var startTime = Recording.UtcNow;

            var containerUrl = new Uri(TestEnvironment.ClassifierTrainingSasUrl);
            var sourceA = new BlobContentSource(containerUrl) { Prefix = "IRS-1040-A/train" };
            var sourceB = new BlobContentSource(containerUrl) { Prefix = "IRS-1040-B/train" };
            var docTypeA = new ClassifierDocumentTypeDetails(sourceA);
            var docTypeB = new ClassifierDocumentTypeDetails(sourceB);
            var docTypes = new Dictionary<string, ClassifierDocumentTypeDetails>()
            {
                { "IRS-1040-A", docTypeA },
                { "IRS-1040-B", docTypeB }
            };

            var options = new BuildClassifierOptions(classifierId, docTypes)
            {
                Description = description
            };

            Operation<DocumentClassifierDetails> operation = null;

            try
            {
                operation = await client.BuildClassifierAsync(WaitUntil.Completed, options);
            }
            finally
            {
                if (operation != null && operation.HasCompleted)
                {
                    await client.DeleteClassifierAsync(classifierId);
                }
            }

            Assert.That(operation.HasCompleted);
            Assert.That(operation.HasValue);

            DocumentClassifierDetails classifier = operation.Value;

            Assert.That(classifier.ClassifierId, Is.EqualTo(classifierId));
            Assert.That(classifier.BaseClassifierId, Is.Null);
            Assert.That(classifier.Description, Is.EqualTo(description));
            Assert.That(classifier.ApiVersion, Is.EqualTo(ServiceVersionString));
            Assert.That(classifier.CreatedOn, Is.GreaterThan(startTime));
            Assert.That(classifier.ExpiresOn, Is.GreaterThan(classifier.CreatedOn));

            DocumentAssert.AreEquivalent(docTypes, classifier.DocumentTypes);

            foreach (var docType in classifier.DocumentTypes.Values)
            {
                Assert.That(docType.SourceKind, Is.Null);
            }
        }

        [RecordedTest]
        public async Task BuildClassifierWithBlobFileListContentSource()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var classifierId = Recording.GenerateId();
            var description = "This classifier was generated by a .NET test.";
            var startTime = Recording.UtcNow;

            var containerUrl = new Uri(TestEnvironment.ClassifierTrainingSasUrl);
            var sourceA = new BlobFileListContentSource(containerUrl, "IRS-1040-A.jsonl");
            var sourceB = new BlobFileListContentSource(containerUrl, "IRS-1040-B.jsonl");
            var docTypeA = new ClassifierDocumentTypeDetails(sourceA);
            var docTypeB = new ClassifierDocumentTypeDetails(sourceB);
            var docTypes = new Dictionary<string, ClassifierDocumentTypeDetails>()
            {
                { "IRS-1040-A", docTypeA },
                { "IRS-1040-B", docTypeB }
            };

            var options = new BuildClassifierOptions(classifierId, docTypes)
            {
                Description = description
            };

            Operation<DocumentClassifierDetails> operation = null;

            try
            {
                operation = await client.BuildClassifierAsync(WaitUntil.Completed, options);
            }
            finally
            {
                if (operation != null && operation.HasCompleted)
                {
                    await client.DeleteClassifierAsync(classifierId);
                }
            }

            Assert.That(operation.HasCompleted);
            Assert.That(operation.HasValue);

            DocumentClassifierDetails classifier = operation.Value;

            Assert.That(classifier.ClassifierId, Is.EqualTo(classifierId));
            Assert.That(classifier.BaseClassifierId, Is.Null);
            Assert.That(classifier.Description, Is.EqualTo(description));
            Assert.That(classifier.ApiVersion, Is.EqualTo(ServiceVersionString));
            Assert.That(classifier.CreatedOn, Is.GreaterThan(startTime));
            Assert.That(classifier.ExpiresOn, Is.GreaterThan(classifier.CreatedOn));

            DocumentAssert.AreEquivalent(docTypes, classifier.DocumentTypes);

            foreach (var docType in classifier.DocumentTypes.Values)
            {
                Assert.That(docType.SourceKind, Is.Null);
            }
        }

        #endregion Build

        #region Copy

        [RecordedTest]
        public async Task CopyClassifierTo()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var classifierId = Recording.GenerateId();
            var description = "This classifier was generated by a .NET test.";
            var tags = new Dictionary<string, string>() { { "tag1", "value1" }, { "tag2", "value2" } };
            var startTime = Recording.UtcNow;

            await using var disposableClassifier = await BuildDisposableDocumentClassifierAsync(description);

            var authorizeCopyOptions = new AuthorizeClassifierCopyOptions(classifierId)
            {
                Description = description
            };

            foreach (var tag in tags)
            {
                authorizeCopyOptions.Tags.Add(tag);
            }

            ClassifierCopyAuthorization copyAuthorization = await client.AuthorizeClassifierCopyAsync(authorizeCopyOptions);

            Operation<DocumentClassifierDetails> operation = null;

            try
            {
                operation = await client.CopyClassifierToAsync(WaitUntil.Completed, disposableClassifier.ClassifierId, copyAuthorization);
            }
            finally
            {
                if (operation != null && operation.HasCompleted)
                {
                    await client.DeleteClassifierAsync(classifierId);
                }
            }

            Assert.That(operation.HasCompleted);
            Assert.That(operation.HasValue);

            DocumentClassifierDetails sourceClassifier = disposableClassifier.Value;
            DocumentClassifierDetails classifier = operation.Value;

            Assert.That(classifier.ClassifierId, Is.EqualTo(classifierId));
            Assert.That(classifier.BaseClassifierId, Is.Null);
            Assert.That(classifier.Description, Is.EqualTo(description));
            Assert.That(classifier.ApiVersion, Is.EqualTo(ServiceVersionString));

            // Add a 4-hour tolerance because the model could have been cached before this test.
            Assert.That(classifier.CreatedOn, Is.GreaterThan(startTime - TimeSpan.FromHours(4)));
            Assert.That(classifier.ExpiresOn, Is.GreaterThan(classifier.CreatedOn));

            DocumentAssert.AreEquivalent(sourceClassifier.DocumentTypes, classifier.DocumentTypes);

            foreach (var docType in classifier.DocumentTypes.Values)
            {
                Assert.That(docType.SourceKind, Is.Null);
            }
        }

        #endregion Copy

        #region Get

        [RecordedTest]
        public async Task GetClassifier()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var description = "This classifier was generated by a .NET test.";

            await using var disposableClassifier = await BuildDisposableDocumentClassifierAsync(description);

            DocumentClassifierDetails expected = disposableClassifier.Value;
            DocumentClassifierDetails classifier = await client.GetClassifierAsync(disposableClassifier.ClassifierId);

            DocumentAssert.AreEqual(expected, classifier);
        }

        [RecordedTest]
        public void GetClassifierCanParseError()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var classifierId = "00000000-0000-0000-0000-000000000000";

            RequestFailedException ex = Assert.ThrowsAsync<RequestFailedException>(async () => await client.GetClassifierAsync(classifierId));

            Assert.That(ex.ErrorCode, Is.EqualTo("NotFound"));
        }

        #endregion

        #region List

        [RecordedTest]
        public async Task GetClassifiers()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var description = "This classifier was generated by a .NET test.";

            await using var disposableClassifier0 = await BuildDisposableDocumentClassifierAsync(description);
            await using var disposableClassifier1 = await BuildDisposableDocumentClassifierAsync(description);

            var expectedIdMapping = new Dictionary<string, DocumentClassifierDetails>()
            {
                { disposableClassifier0.ClassifierId, disposableClassifier0.Value },
                { disposableClassifier1.ClassifierId, disposableClassifier1.Value }
            };
            var idMapping = new Dictionary<string, DocumentClassifierDetails>();

            await foreach (DocumentClassifierDetails classifier in client.GetClassifiersAsync())
            {
                if (expectedIdMapping.ContainsKey(classifier.ClassifierId))
                {
                    idMapping.Add(classifier.ClassifierId, classifier);
                }

                if (idMapping.Count == expectedIdMapping.Count)
                {
                    break;
                }
            }

            Assert.That(idMapping.Count, Is.EqualTo(expectedIdMapping.Count));

            foreach (string id in expectedIdMapping.Keys)
            {
                Assert.That(idMapping, Contains.Key(id));

                DocumentClassifierDetails expected = expectedIdMapping[id];
                DocumentClassifierDetails classifier = idMapping[id];

                DocumentAssert.AreEqual(expected, classifier);
            }
        }

        #endregion List

        #region Delete

        [RecordedTest]
        public async Task DeleteClassifier()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();

            var disposableClassifier = await BuildDisposableDocumentClassifierAsync();
            var response = await client.DeleteClassifierAsync(disposableClassifier.ClassifierId);

            Assert.That(response.Status, Is.EqualTo((int)HttpStatusCode.NoContent));
        }

        [RecordedTest]
        public void DeleteClassifierCanParseError()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var classifierId = "00000000-0000-0000-0000-000000000000";

            RequestFailedException ex = Assert.ThrowsAsync<RequestFailedException>(async () => await client.DeleteClassifierAsync(classifierId));

            Assert.That(ex.ErrorCode, Is.EqualTo("NotFound"));
        }

        #endregion Delete

        [RecordedTest]
        public async Task AuthorizeClassifierCopy()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var classifierId = Recording.GenerateId();
            var options = new AuthorizeClassifierCopyOptions(classifierId);

            ClassifierCopyAuthorization copyAuthorization = await client.AuthorizeClassifierCopyAsync(options);

            Assert.That(copyAuthorization.TargetClassifierId, Is.EqualTo(classifierId));
            Assert.That(copyAuthorization.TargetClassifierLocation.AbsoluteUri, Does.StartWith(TestEnvironment.Endpoint));
            Assert.That(copyAuthorization.TargetResourceId, Is.EqualTo(TestEnvironment.ResourceId));
            Assert.That(copyAuthorization.TargetResourceRegion, Is.EqualTo(TestEnvironment.ResourceRegion));
            Assert.That(copyAuthorization.AccessToken, Is.Not.Null);
            Assert.That(copyAuthorization.AccessToken, Is.Not.Empty);
            Assert.That(copyAuthorization.ExpiresOn, Is.GreaterThan(Recording.UtcNow));
        }
    }
}
